`include "mycpu.h"
module wb_stage(
    input clk,
    input reset,
    //WB allowin
    output ws_allowin,
    //MEM to WB
    input ms_to_ws_valid,
    input [`MS_TO_WS_WD - 1:0] ms_to_ws_bus,
    //WB to ID
    output [`WS_TO_RF_WD - 1:0] ws_to_rf_bus,
    output out_ws_valid,
    //task12,13:
    output exec_flush,
    output [31:0] ws_pc_gen_exec,
    //debug
    output [31:0] debug_wb_rf_pc,
    output [3:0] debug_wb_rf_we,
    output [4:0] debug_wb_rf_wnum,
    output [31:0] debug_wb_rf_wdata,
    //tlbsrch
    input es_tlbsrch_en,
    output [3:0] es_tlbsrch_index,
    output es_tlbsrch_hit,
    //invtlb
    input es_invtlb_en,
    input [31:0] es_invtlb_rj,
    input [31:0] es_invtlb_rk,
    input [4:0] es_invtlb_op,
    //tlb blk bus
    output [`WS_TLB_BLK_BUS_WD - 1:0] ws_tlb_blk_bus
);

reg ws_valid;
wire ws_ready_go;
reg [`MS_TO_WS_WD - 1:0] ms_to_ws_bus_r;
wire [31:0] ws_pc;
wire [31:0] ws_final_result;
wire [31:0] ws_rf_wdata;
wire [4:0] ws_dest;
wire ws_gr_we;
wire ws_rf_we;
//exp12
wire ws_is_priv;
wire ws_inst_csrrd;
wire ws_inst_csrwr;
wire ws_inst_csrxchg;
wire ws_inst_syscall;
wire [14:0] ws_ex_code;
wire ws_inst_ertn;
wire ws_csr_re, ws_csr_we;
wire [13:0] ws_csr_num_inst;
wire [13:0] ws_csr_num;
wire [31:0] ws_rj_value;
wire [31:0] ws_csr_wmask;
wire [31:0] ws_csr_rvalue;
wire [31:0] ws_csr_wdata;
wire [31:0] ws_ex_entry;
wire ws_wb_ex;
wire [5:0] ws_ecode;
wire [8:0] ws_esubcode;
//exp13:
wire ws_ex_adef, ws_ex_ine, ws_ex_ale;
wire [31:0] ws_ex_baddr;
wire ws_inst_brk;
wire ws_inst_rdcntid;
wire ws_inst_rdcntvl_w;
wire ws_inst_rdcntvh_w;
wire ws_has_int;
reg ws_has_int_r;
//exp18:tlb
wire ws_tlbsrch_hit;
wire [3:0] ws_tlbsrch_index;
wire ws_inst_tlbrd;
wire ws_inst_tlbwr;
wire ws_inst_tlbfill;
wire ws_inst_tlbsrch;
wire ws_refetch_en;
/* tlb wires */
    wire do_tlbrd;
    wire do_tlbwr;
    wire do_invtlb;
    wire do_tlbsrch;
//search port 0 (for fetch)
    wire [18:0] s0_vppn;
    wire s0_va_bit12;
    wire [9:0] s0_asid;
    wire s0_found;
    wire [3:0] s0_index;
    wire [19:0] s0_ppn;
    wire [5:0] s0_ps;
    wire [1:0] s0_plv;
    wire [1:0] s0_mat;
    wire s0_d;
    wire s0_v;
//search port 1 (for fetch)
    wire [18:0] s1_vppn;
    wire s1_va_bit12;
    wire [9:0] s1_asid;
    wire s1_found;
    wire [3:0] s1_index;
    wire [19:0] s1_ppn;
    wire [5:0] s1_ps;
    wire [1:0] s1_plv;
    wire [1:0] s1_mat;
    wire s1_d;
    wire s1_v;
//invtlb opcode
    wire invtlb_valid;
    wire [4:0] invtlb_op;
//write port
    wire we;
    wire [3:0] w_index;
    wire [3:0] csr_tlb_index;
    wire w_e;
    wire w_ne;
    wire [18:0] w_vppn;
    wire [5:0] w_ps;
    wire [9:0] w_asid;
    wire w_g;
    wire w_g1, w_g2;
    wire [19:0] w_ppn0;
    wire [1:0] w_plv0;
    wire [1:0] w_mat0;
    wire w_d0;
    wire w_v0;
    wire [19:0] w_ppn1;
    wire [1:0] w_plv1;
    wire [1:0] w_mat1;
    wire w_d1;
    wire w_v1;
//read port
    wire [3:0] r_index;
    wire r_e;
    wire [18:0] r_vppn;
    wire [5:0] r_ps;
    wire [9:0] r_asid;
    wire r_g;
    wire [19:0] r_ppn0;
    wire [1:0] r_plv0;
    wire [1:0] r_mat0;
    wire r_d0;
    wire r_v0;
    wire [19:0] r_ppn1;
    wire [1:0] r_plv1;
    wire [1:0] r_mat1;
    wire r_d1;
    wire r_v1;
reg  [ 3:0] random;

assign out_ws_valid = ws_valid;

assign ws_ready_go = 1'b1;

assign ws_allowin = !ws_valid || ws_ready_go;

always @(posedge clk) begin
    if(reset)
        ws_valid <= 1'b0;
    else if(exec_flush)
        ws_valid <= 1'b0;
    else if(ws_allowin)
        ws_valid <= ms_to_ws_valid;
end

always @(posedge clk) begin
    if(ws_allowin && ms_to_ws_valid) 
        ms_to_ws_bus_r <= ms_to_ws_bus;
end

assign {
    ws_tlbsrch_hit,
    ws_tlbsrch_index[3:0],
    ws_inst_tlbsrch,
    ws_inst_tlbrd,
    ws_inst_tlbwr,
    ws_inst_tlbfill,
    ws_ex_adef,
    ws_ex_ine,
    ws_ex_ale,
    ws_ex_baddr[31:0],
    ws_inst_brk,
    ws_inst_rdcntid,
    ws_inst_rdcntvl_w,
    ws_inst_rdcntvh_w,
    ws_ex_code[14:0],
    ws_rj_value[31:0],
    ws_csr_wdata[31:0],
    ws_inst_syscall,
    ws_inst_ertn,
    ws_inst_csrrd,
    ws_inst_csrwr,
    ws_inst_csrxchg,
    ws_csr_num_inst[13:0],
    ws_pc[31:0],
    ws_gr_we,
    ws_dest[4:0],
    ws_final_result[31:0] } = ms_to_ws_bus_r[`MS_TO_WS_WD - 1:0];


//WB to RF
assign ws_rf_we = ws_gr_we & ws_valid & ~ws_wb_ex;

assign ws_to_rf_bus[`WS_TO_RF_WD - 1:0] = {
    ws_rf_we,               //37:37
    ws_dest[4:0],           //36:32
    ws_rf_wdata[31:0]   //31:0
};
                
assign ws_rf_wdata = ws_is_priv ? ws_csr_rvalue[31:0] : ws_final_result[31:0];

assign ws_is_priv = ws_inst_csrrd | ws_inst_csrwr | ws_inst_csrxchg | ws_inst_ertn
                |   ws_inst_syscall | ws_inst_brk | ws_inst_rdcntid | ws_inst_rdcntvh_w
                |   ws_inst_rdcntvl_w;

assign ws_csr_re = (ws_inst_csrrd 
                  | ws_inst_csrxchg 
                  | ws_inst_csrwr
                  | ws_inst_rdcntid
                  | ws_inst_rdcntvl_w
                  | ws_inst_rdcntvh_w) & ws_valid;
assign ws_csr_we = (ws_inst_csrwr | ws_inst_csrxchg) & ws_valid;

assign ws_ecode = {6{ws_valid & ws_has_int}} & `ECODE_INT
                | {6{ws_valid & ws_ex_adef}} & `ECODE_ADE
                | {6{ws_valid & ws_ex_ale}} & `ECODE_ALE
                | {6{ws_valid & ws_inst_syscall}} & `ECODE_SYS
                | {6{ws_valid & ws_inst_brk}} & `ECODE_BRK
                | {6{ws_valid & ws_ex_ine}} & `ECODE_INE;
                
assign ws_esubcode = 9'h000;

assign ws_wb_ex = (ws_inst_syscall 
                 | ws_inst_brk 
                 | ws_has_int
                 | ws_ex_adef
                 | ws_ex_ine
                 | ws_ex_ale) & ws_valid;

assign exec_flush = (ws_inst_syscall 
                   | ws_inst_ertn
                   | ws_inst_brk
                   | ws_ex_adef
                   | ws_ex_ine
                   | ws_ex_ale
                   | ws_has_int
                   | ws_refetch_en) & ws_valid;

assign ws_refetch_en = (ws_inst_tlbrd | ws_inst_tlbfill | ws_inst_tlbwr) & ws_valid;

assign ws_pc_gen_exec = {32{ws_inst_ertn}} & ws_csr_rvalue
                    |   {32{exec_flush & ~ws_inst_ertn & ~ws_refetch_en}} & ws_ex_entry
                    |   {32{ws_refetch_en}} & (ws_pc + 32'h4);

assign ws_csr_num = {14{ws_inst_ertn}} & `CSR_ERA
                 |  {14{ws_inst_rdcntvl_w | ws_inst_rdcntvh_w}} & `CSR_TVAL
                 |  {14{ws_inst_rdcntid}} & `CSR_TID
                 |  {14{~ws_inst_ertn & ~ws_inst_rdcntid & ~ws_inst_rdcntvl_w
                     &  ~ws_inst_rdcntvh_w}} & ws_csr_num_inst;

assign ws_csr_wmask = ws_inst_csrxchg ? ws_rj_value : 32'hffffffff;

csr u_csr(
    .clk(clk),
    .reset(reset),
    .csr_re(ws_csr_re),
    .csr_we(ws_csr_we),
    .csr_num(ws_csr_num),
    .csr_rvalue(ws_csr_rvalue),
    .csr_wmask(ws_csr_wmask),
    .csr_wvalue(ws_csr_wdata),
    .wb_ex(ws_wb_ex),
    .wb_pc(ws_pc),
    .wb_ecode(ws_ecode),
    .wb_esubcode(ws_esubcode),
    .wb_vaddr(ws_ex_baddr),
    .ertn_flush(ws_inst_ertn),
    .has_int(ws_has_int),
    .ex_entry(ws_ex_entry),
    .hw_int_in(8'b0),
    .ipi_int_in(1'b0),
    .coreid_in(32'b0),
//tlbrd
    .r_index(r_index),
    .tlbrd_we(do_tlbrd),
    .r_e(r_e),
    .r_vppn(r_vppn),
    .r_ps(r_ps),
    .r_asid(r_asid),
    .r_g(r_g),
    .r_ppn0(r_ppn0),
    .r_plv0(r_plv0),
    .r_mat0(r_mat0),
    .r_d0(r_d0),
    .r_v0(r_v0),
    .r_ppn1(r_ppn1),
    .r_plv1(r_plv1),
    .r_mat1(r_mat1),
    .r_d1(r_d1),
    .r_v1(r_v1),
//tlbwr
    .csr_tlbidx_index(csr_tlb_index),
    .csr_tlbidx_ne(w_ne),
    .csr_tlbehi_vppn(w_vppn),
    .csr_tlbidx_ps(w_ps),
    .csr_asid_asid(w_asid),
    .csr_tlbelo1_g(w_g1),
    .csr_tlbelo2_g(w_g2),
    .csr_tlbelo1_ppn(w_ppn0),
    .csr_tlbelo1_plv(w_plv0),
    .csr_tlbelo1_mat(w_mat0),
    .csr_tlbelo1_d(w_d0),
    .csr_tlbelo1_v(w_v0),
    .csr_tlbelo2_ppn(w_ppn1),
    .csr_tlbelo2_plv(w_plv1),
    .csr_tlbelo2_mat(w_mat1),
    .csr_tlbelo2_d(w_d1),
    .csr_tlbelo2_v(w_v1),
//tlbsrch
    .tlbsrch_en(do_tlbsrch),
    .tlbsrch_hit(s1_found),
    .tlbsrch_index(s1_index)
);

assign debug_wb_rf_pc = ws_pc;
assign debug_wb_rf_we = {4{ws_rf_we}};
assign debug_wb_rf_wnum = ws_dest;
assign debug_wb_rf_wdata = ws_rf_wdata;

//ws to ds bus (tlb blocking)
assign ws_tlb_blk_bus = {ws_inst_tlbwr, ws_inst_tlbfill, ws_inst_tlbrd, 
                         ws_inst_csrwr | ws_inst_csrxchg, ws_csr_num[13:0]};
assign out_ws_valid = ws_valid;
//tlb enable signal
assign do_tlbrd = ws_inst_tlbrd & ws_valid;
assign do_tlbsrch = es_tlbsrch_en;
assign do_invtlb = es_invtlb_en;
//tlb write port
assign w_e = ~w_ne;
assign w_g = w_g1 & w_g2;
assign we = (ws_inst_tlbwr || ws_inst_tlbfill) & ws_valid;
always @ (posedge clk) begin
    if (reset)
        random <= 4'd8;
    else if(we && ws_inst_tlbfill)
        random <= random + 1'b1;
end
assign w_index = ws_inst_tlbwr ? csr_tlb_index : random;//{$random} % 16;
//tlb search port-1
assign s1_vppn = do_tlbsrch ? w_vppn :
                 (do_invtlb && (es_invtlb_op == 5'h05 || es_invtlb_op == 5'h06)) ? es_invtlb_rk[31:13] :
                 19'b0;
assign s1_va_bit12 = (do_invtlb && (es_invtlb_op == 5'h05 || es_invtlb_op == 5'h06)) ? es_invtlb_rk[12] :
                     1'b0;
assign s1_asid = do_invtlb ? es_invtlb_rj[9:0] : w_asid;

tlb u_tlb(
    .clk(clk),
//search port 0
    .s0_vppn(s0_vppn),
    .s0_va_bit12(s0_va_bit12),
    .s0_asid(s0_asid),
    .s0_found(s0_found),
    .s0_index(s0_index),
    .s0_ppn(s0_ppn),
    .s0_ps(s0_ps),
    .s0_plv(s0_plv),
    .s0_mat(s0_mat),
    .s0_d(s0_d),
    .s0_v(s0_v),
//search port 1
    .s1_vppn(s1_vppn),
    .s1_va_bit12(s1_va_bit12),
    .s1_asid(s1_asid),
    .s1_found(s1_found),
    .s1_index(s1_index),
    .s1_ppn(s1_ppn),
    .s1_ps(s1_ps),
    .s1_plv(s1_plv),
    .s1_mat(s1_mat),
    .s1_d(s1_d),
    .s1_v(s1_v),
//invtlb code 
    .invtlb_valid(do_invtlb),
    .invtlb_op(es_invtlb_op),
//write port
    .we(we),
    .w_index(w_index),
    .w_e(w_e),
    .w_vppn(w_vppn),
    .w_ps(w_ps),
    .w_asid(w_asid),
    .w_g(w_g),
    .w_ppn0(w_ppn0),
    .w_plv0(w_plv0),
    .w_mat0(w_mat0),
    .w_d0(w_d0),
    .w_v0(w_v0),
    .w_ppn1(w_ppn1),
    .w_plv1(w_plv1),
    .w_mat1(w_mat1),
    .w_d1(w_d1),
    .w_v1(w_v1),
//read port
    .r_index(r_index),
    .r_e(r_e),
    .r_vppn(r_vppn),
    .r_ps(r_ps),
    .r_asid(r_asid),
    .r_g(r_g),
    .r_ppn0(r_ppn0),
    .r_plv0(r_plv0),
    .r_mat0(r_mat0),
    .r_d0(r_d0),
    .r_v0(r_v0),
    .r_ppn1(r_ppn1),
    .r_plv1(r_plv1),
    .r_mat1(r_mat1),
    .r_d1(r_d1),
    .r_v1(r_v1)
);

endmodule
